﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

namespace IOP.Models.Queue
{
    /// <summary>
    /// 循环队列
    /// </summary>
    public class LoopQueue<T> : IEnumerable<T>
    {
        /// <summary>
        /// 队列大小
        /// </summary>
        private int _Size;

        /// <summary>
        /// 首下标
        /// </summary>
        private volatile int _Front;

        /// <summary>
        /// 尾下标
        /// </summary>
        private volatile int _Rear;

        /// <summary>
        /// 存储数组
        /// </summary>
        private T[] _Arrays { get; set; }

        /// <summary>
        /// 队列元素数量
        /// </summary>
        public volatile int Count;

        /// <summary>
        /// 队列是否为空
        /// </summary>
        public bool IsEmpty => Count == 0;

        /// <summary>
        /// 队列是否已满
        /// </summary>
        public bool IsFull => Count == _Size;

        /// <summary>
        /// 当队列存在元素时触发事件
        /// </summary>
        public event Action<LoopQueue<T>> OnQueueHasElements;

        /// <summary>
        /// 当队列为满时触发事件
        /// </summary>
        public event Action<LoopQueue<T>> OnQueueIsFull;

        /// <summary>
        /// 线程锁
        /// </summary>
        private SpinLock SyncRoot = new SpinLock();

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="size"></param>
        public LoopQueue(int size = 512)
        {
            _Size = size;
            _Arrays = new T[_Size];
            _Rear = _Front = 0;
            Count = 0;
        }

        /// <summary>
        /// 入队
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Enqueue(T item)
        {
            bool getLook = false;
            bool empty = false;
            try
            {
                SyncRoot.Enter(ref getLook);
                if (IsFull)
                {
                    OnQueueIsFull?.Invoke(this);
                    if (IsFull) throw new Exception("this queue is full");

                }
                empty = IsEmpty;
                _Arrays[_Rear] = item;
                _Rear = (_Rear + 1) % _Size;
                Count++;
                return true;
            }
            finally
            {
                if (getLook) SyncRoot.Exit();
                if (empty) OnQueueHasElements?.Invoke(this);
            }
        }

        /// <summary>
        /// 出队
        /// </summary>
        /// <returns></returns>
        public bool Dequene(out T item)
        {
            bool getLook = false;
            try
            {
                SyncRoot.Enter(ref getLook);
                item = default;
                if (IsEmpty) return false;
                item = _Arrays[_Front];
                _Front = (_Front + 1) % _Size;
                Count--;
                return true;
            }
            finally
            {
                if (getLook) SyncRoot.Exit();
            }
        }

        /// <summary>
        /// 清理
        /// </summary>
        public void Clear()
        {
            bool getLook = false;
            try
            {
                SyncRoot.Enter(ref getLook);
                _Arrays = new T[_Size];
                _Front = _Rear = 0;
                _Size = 0;
                Count = 0;
            }
            finally
            {
                if (getLook) SyncRoot.Exit();
            }
        }

        /// <summary>
        /// 返回首元素但不移出队列
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Peek(out T item)
        {
            bool getLook = false;
            try
            {
                SyncRoot.Enter(ref getLook);
                item = default;
                if (IsEmpty) return false;
                item = _Arrays[_Front];
                return true;
            }
            finally
            {
                if (getLook) SyncRoot.Exit();
            }
        }

        /// <summary>
        /// 扩容
        /// </summary>
        /// <param name="size"></param>
        /// <returns></returns>
        public bool Dilatation(int size)
        {
            bool getLook = false;
            try
            {
                if (size < _Size) return false;
                if(!SyncRoot.IsHeld) SyncRoot.Enter(ref getLook);
                _Size = size;
                var newArray = new T[_Size];
                _Arrays.CopyTo(newArray, 0);
                _Arrays = newArray;
                return true;
            }
            finally
            {
                if (getLook) SyncRoot.Exit();
            }
        }

        /// <summary>
        /// 执行遍历
        /// </summary>
        /// <returns></returns>
        public IEnumerator<T> GetEnumerator()
        {
            foreach (var item in _Arrays)
            {
                yield return item;
            }
        }

        /// <summary>
        /// 执行遍历
        /// </summary>
        /// <returns></returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            foreach (var item in _Arrays)
            {
                yield return item;
            }
        }
    }
}
